package com.mhtool.core.math;

import lombok.experimental.UtilityClass;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.function.Function;

import static java.math.BigDecimal.ROUND_HALF_UP;
import static java.math.BigDecimal.ZERO;

/**
 * @author yin
 * @date 2021/12/27 17:20
 * @since 8.0.0
 */
@UtilityClass
public class BigDecimalUtils {
    public static BigDecimal isNull(BigDecimal n) {
        return null != n ? n : ZERO;
    }

    public static boolean isNullOrZero(BigDecimal i) {
        if (i == null)
            return true;
        return i.compareTo(ZERO) == 0;
    }

    public static boolean eq(BigDecimal a, BigDecimal b) {
        if (null == a && null == b) {
            return true;
        }
        if (null == a || null == b) {
            return false;
        }
        return a.compareTo(b) == 0;
    }

    /**
     * > 0
     */
    public static boolean gtZero(BigDecimal n) {
        return null != n && n.compareTo(ZERO) > 0;
    }

    /**
     * < 0
     */
    public static boolean ltZero(BigDecimal n) {
        return null != n && n.compareTo(ZERO) < 0;
    }

    /**
     * != 0
     */
    public static boolean neZero(BigDecimal n) {
        return null != n && n.compareTo(ZERO) != 0;
    }

    public static BigDecimal castBigDecimal(Object o) {
        if (null == o)
            return ZERO;
        if (o instanceof Integer)
            return BigDecimal.valueOf((Integer) o);
        if (o instanceof Long)
            return BigDecimal.valueOf((Long) o);
        if (o instanceof Double)
            return BigDecimal.valueOf((Double) o);
        if (o instanceof BigInteger) {
            return new BigDecimal((BigInteger) o);
        }
        if (o instanceof String) {
            try {
                return new BigDecimal((String) o);
            } catch (NumberFormatException e) {
                e.printStackTrace();
                return ZERO;
            }
        }
        throw new NumberFormatException("非法类型");
    }

    public static <T> BigDecimal sum(List<T> list, Function<T, BigDecimal> function) {
        BigDecimal sum = BigDecimal.ZERO;
        for (T o : list) {
            sum = add(sum, function.apply(o));
        }
        return sum;
    }

    public static BigDecimal add(BigDecimal a, BigDecimal b) {
        a = isNull(a);
        b = isNull(b);
        return a.add(b);
    }

    public static BigDecimal add(BigDecimal a, BigDecimal b, BigDecimal... c) {
        a = add(a, b);
        for (BigDecimal n : c) {
            a = a.add(isNull(n));
        }
        return a;
    }

    public static BigDecimal subtract(BigDecimal a, BigDecimal b) {
        a = isNull(a);
        b = isNull(b);
        return a.subtract(b);
    }

    public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
        a = isNull(a);
        b = isNull(b);
        return a.multiply(b);
    }

    public static BigDecimal multiply(BigDecimal a, Integer b) {
        BigDecimal b1 = null == b ? null : BigDecimal.valueOf(b);
        return multiply(a, b1);
    }

    public static BigDecimal multiply(BigDecimal a, Double b) {
        BigDecimal b1 = null == b ? null : BigDecimal.valueOf(b);
        return multiply(a, b1);
    }

    public static BigDecimal convert2BigDecimal(Number a) {
        if (null == a) {
            return ZERO;
        }
        if (a instanceof BigDecimal)
            return (BigDecimal) a;
        if (a instanceof Integer) {
            return BigDecimal.valueOf(a.intValue());
        }
        if (a instanceof Double) {
            return BigDecimal.valueOf(a.doubleValue());
        }
        return ZERO;
    }

    public static BigDecimal divide(BigDecimal a, BigDecimal b) {
        return divide(a, b, 10);
    }

    public static BigDecimal divide(BigDecimal a, BigDecimal b, int scale) {
        return isNull(a).divide(isNull(b), scale, ROUND_HALF_UP);
    }

    public static BigDecimal min(BigDecimal a, BigDecimal b) {
        a = isNull(a);
        b = isNull(b);
        return a.compareTo(b) > 0 ? b : a;
    }

    public static BigDecimal max(BigDecimal a, BigDecimal b) {
        a = isNull(a);
        b = isNull(b);
        return a.compareTo(b) < 0 ? b : a;
    }

    public BigDecimal setScale(BigDecimal decimal, int newScale) {
        return setScale(decimal, newScale, ROUND_HALF_UP);
    }

    public BigDecimal setScale(BigDecimal decimal, int newScale, int roundingMode) {
        if (null == decimal) {
            return ZERO;
        }
        return decimal.setScale(newScale, roundingMode);
    }

    public static class Builder {
        private BigDecimal result;

        public Builder(BigDecimal first) {
            this.result = isNull(first);
        }

        public Builder add(BigDecimal n) {
            result = result.add(isNull(n));
            return this;
        }

        public Builder subtract(BigDecimal n) {
            result = result.subtract(isNull(n));
            return this;
        }

        public Builder multiply(BigDecimal multiplicand) {
            result = result.multiply(isNull(multiplicand));
            return this;
        }

        public Builder multiply(Integer multiplicand) {
            return multiply(null == multiplicand ? null : BigDecimal.valueOf(multiplicand));
        }

        public Builder divide(BigDecimal divisor) {
            return divide(divisor, 10);
        }

        public Builder divide(int divisor) {
            return divide(BigDecimal.valueOf(divisor));
        }

        public Builder divide(BigDecimal divisor, int scale) {
            Assert.isTrue(!isNullOrZero(divisor), "The divisor must not be zero");
            Assert.isTrue(scale >= 0, "The scale must be a positive integer or zero");
            result = result.divide(divisor, scale, ROUND_HALF_UP);
            return this;
        }

        public Builder divide(int divisor, int scale) {
            return divide(BigDecimal.valueOf(divisor), scale);
        }

        public BigDecimal value() {
            return result;
        }

        public double doubleValue() {
            return result.doubleValue();
        }

        public int intValue() {
            return result.intValue();
        }

        public long longValue() {
            return result.longValue();
        }
    }

    public static Builder of(BigDecimal n) {
        return new Builder(n);
    }
}

